#pragma once
#include <algorithm>
#include <chrono>
#include <functional>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <random>
#include <string>
#include <vector>

#ifdef _WIN32
#define COLOR(x) ""
#define RESET  ""
#else
#define COLOR(x) "\033[" #x "m"
#define RESET  "\033[0m"
#endif

struct Stats {
    std::string name;
    double      time_ns = 0;
    uint64_t    cmp_cnt = 0;
    uint64_t    swap_cnt = 0;
};

template <typename Gen>
std::vector<int> make_data(std::size_t n, Gen &&g) {
    std::vector<int> v(n);
    std::generate(v.begin(), v.end(), g);
    return v;
}

inline std::string pretty_time(double ns) {
    if (ns < 1000) return std::to_string((int)ns) + " ns";
    if (ns < 1'000'000) return std::to_string((int)(ns / 1'000)) + " μs";
    return std::to_string((int)(ns / 1'000'000)) + " ms";
}

template <typename SortFunc>
Stats benchmark(const std::string &label, SortFunc &&sort, std::vector<int> v) {
    Stats s{.name = label};
    const auto t0 = std::chrono::high_resolution_clock::now();
    sort(v);
    const auto t1 = std::chrono::high_resolution_clock::now();
    s.time_ns = std::chrono::duration<double, std::nano>(t1 - t0).count();
    if (!std::is_sorted(v.begin(), v.end())) {
        std::cerr << COLOR(31) << label << " failed verification!" RESET << std::endl;
        std::terminate();
    }
    return s;
}

inline void print_header(std::size_t n) {
    std::cout << COLOR(1;34) << "└─ Data size = " << n << RESET << std::endl;
    std::cout << COLOR(1;33) << std::left << std::setw(18) << "Algorithm"
              << std::right << std::setw(12) << "Time"
              << std::setw(12) << "Cmp#"
              << std::setw(12) << "Swap#" << RESET << std::endl;
}

inline void print_stat(const Stats &s) {
    std::cout << std::left << std::setw(18) << s.name
              << std::right << std::setw(12) << pretty_time(s.time_ns)
              << std::setw(12) << s.cmp_cnt
              << std::setw(12) << s.swap_cnt << std::endl;
}